class: title-slide # ER018 - Analyzing Business Relations & Documents ## PVA1 ### Arbeiten mit Zeichenketten (strings) <br> <br> <br> <br> <br> <br> <br> ### FS 2024 <br> ### Prof. Dr. Jörg Schoder .mycontacts[
@FFHS-EconomicResearch
@jfschoder ] --- layout: true <div class="my-footer"></div> <div style="position: absolute;left:400px;bottom:10px;font-size:9px">
Prof. Dr. Jörg Schoder</div> --- name: agenda class: left .blockquote[Agenda] ## Arbeiten mit Zeichenketten (strings) * (Sehr kurze) Einführung in R-Objekte * Zeichenketten in Base-R und das stringr-Paket * Regelhafte Ausdrücke (regular expressions) * Fortgeschrittene reguläre Ausdrücke --- class: left .blockquote[Intro R-Objekte] ## R als objektorientierte Sprache * Objekte lassen sich in Klassen einteilen * tibbles, data.frames * Vektoren * Funktionen -- * Objekte sind vektorbasiert -- * Variablentypen -- * logisch (`logical`) -- * numerisch * `integer` * `double` -- * kategorial * `character` (Zeichenketten, engl. strings) * `factor`
vgl. ausführlicher <a name=cite-wickham_r_2023></a>[Wickham, Çetinkaya-Rundel, and Grolemund (2023)](#bib-wickham_r_2023), Kapitel 12ff. ??? * logical: binäre Logik (true vs. false) * numerisch: integer (ganze Zahlen), double (Doppelgleitkommazahlen) * kategoriale Daten * strings (Zeichenketten) * factors für überschaubar viele Ausprägungen, die nich alphanumerisch geordnet werden sollen --- class: inverse, center, middle ## Zeichenketten in Base-R und das stringr-Paket .blockquote[strings erzeugen] .blockquote[strings verknüpfen] .blockquote[strings zerlegen] --- class: left .blockquote[strings erzeugen] ## Eingabe mit Anführungzeichen * Einfache Anführungszeichen ```r "hoi!" ``` ``` ## [1] "hoi!" ``` * Doppelte Anführungszeichen ```r 'hoi!' ``` ``` ## [1] "hoi!" ``` * Problem: verschachtelte Anführungszeichen ```r # Ich habe "hoi!" gesagt "Ich habe "hoi!" gesagt." ``` ``` ## Error: <text>:2:12: unerwartetes Symbol ## 1: # Ich habe "hoi!" gesagt ## 2: "Ich habe "hoi ## ^ ``` --- class: left .blockquote[strings erzeugen] ## Escape-Sequenz * Verschachtelte Anführungszeichen: * Variante 1 (`'`) ```r 'Ich habe "hoi!" gesagt.' ``` ``` ## [1] "Ich habe \"hoi!\" gesagt." ``` * Variante 2 `\"` (Escape Sequenz!) ```r "Ich habe \"hoi!\" gesagt." ``` ``` ## [1] "Ich habe \"hoi!\" gesagt." ``` --- class: left .blockquote[strings erzeugen] ## Weitere Anwendungsfälle für Escape Sequenzen * Neue Zeile: `\n` ```r "Mit dem Pfeil, dem Bogen Durch Gebirg und Tal Kommt der Schütz gezogen Früh am Morgenstrahl." ``` ``` ## [1] "Mit dem Pfeil, dem Bogen\nDurch Gebirg und Tal\nKommt der Schütz gezogen\nFrüh am Morgenstrahl." ``` * Unicode-Symbole ([Unicode-Chart](https://www.unicode.org/charts/)): * 8 Hexcode-Symbole: `\U` * 8 Hexcode-Symbole: `\u` ??? Escape sequences You might have been surprised at the output from the last part of the last exercise. How did you get two lines from one string, and how did you get that little globe? The key is the \. A sequence in a string that starts with a \ is called an escape sequence and allows us to include special characters in our strings. You saw one escape sequence in the first exercise: \" is used to denote a double quote. In "hello\n\U1F30D" there are two escape sequences: \n gives a newline, and \U followed by up to 8 hex digits sequence denotes a particular Unicode character. Unicode is a standard for representing characters that might not be on your keyboard. Each available character has a Unicode code point: a number that uniquely identifies it. These code points are generally written in hex notation, that is, using base 16 and the digits 0-9 and A-F. You can find the code point for a particular character by looking up a code chart. If you only need four digits for the codepoint, an alternative escape sequence is \u. When R comes across a `\` it assumes you are starting an escape, so if you actually need a backslash in your string you'll need the sequence `\\`. --- class: left ## Zahlenformate .pull-left[ ```r x <- pi x ``` ``` ## [1] 3.141593 ``` * `format()`: fix vs. wissenschaftlich ```r format(x, scientific = TRUE) ``` ``` ## [1] "3.141593e+00" ``` * `as.character()` ```r as.character(x) ``` ``` ## [1] "3.14159265358979" ``` ??? wissenschaftlich: `\(3,1415*10^0\)` --- class: left .blockquote[strings verknüpfen] ## `paste()`-Funktion ```r paste("H","O","P","P") ``` ``` ## [1] "H O P P" ``` ```r paste("H","O","P","P", sep = "-") ``` ``` ## [1] "H-O-P-P" ``` ```r paste(c("Hey","Ach","Mach"), "Du") ``` ``` ## [1] "Hey Du" "Ach Du" "Mach Du" ``` --- class: left .blockquote[strings verknüpfen] ## Old MacDonald ```r old_mac <- function(animal, animal_goes){ eieio <- paste("E", "I", "E", "I", "O", sep = "-") old_mac <- "Old MacDonald had a farm" writeLines(c( old_mac, eieio, paste("And on his farm he had a", animal), eieio, paste(c("Here", "There", "Everywhere"), "a", c(animal_goes, animal_goes, paste(rep(animal_goes, 2), collapse = "-")), collapse = ", "), old_mac, eieio)) } ``` --- class: left .blockquote[strings verknüpfen] ## Old MacDonald ```r old_mac("cow","moo") ``` ``` ## Old MacDonald had a farm ## E-I-E-I-O ## And on his farm he had a cow ## E-I-E-I-O ## Here a moo, There a moo, Everywhere a moo-moo ## Old MacDonald had a farm ## E-I-E-I-O ``` ```r old_mac("dog","wolf") ``` ``` ## Old MacDonald had a farm ## E-I-E-I-O ## And on his farm he had a dog ## E-I-E-I-O ## Here a wolf, There a wolf, Everywhere a wolf-wolf ## Old MacDonald had a farm ## E-I-E-I-O ``` --- class: left .blockquote[strings zerlegen] ## stringr-Paket * Leistungsstark, aber leicht zu erlernen * Aufbauend auf stringi * Prägnant und konsistent * Alle Funktionen beginnen mit `str_` * Alle Funktionen nehmen einen Vektor von Strings als erstes Argument --- class: left .blockquote[strings zerlegen] ## stringr-Vignette <iframe src="https://stringr.tidyverse.org/" width="100%" height="410px" data-external="1"></iframe> --- class: inverse, center, middle ## Regelhafte Ausdrücke .blockquote[Regelhafte Ausdrücke mit dem rebus-Paket] .blockquote[Abwechselnde Zeichenfolgen (Alternations)] .blockquote[Zeichenklassen (Character classes)] .blockquote[Wiederholungen] .blockquote[Abkürzungen] --- class: left .blockquote[Regelhafte Ausdrücke mit dem rebus-Paket] ## Regelhafte Ausdrücke ??? * regular expressions (regexp) --- class: left .blockquote[Regelhafte Ausdrücke mit dem rebus-Paket] ## Regelhafte Ausdrücke * Übersicht | Muster | Regelmäßiger Ausdruck | rebus | |----------------------|:---------------------:|:----------:| | Beginn eines strings | `^` | `START` | | Ende eines strings | `$` | `END` | | Einzelne Symbole | `.` | `ANY_CHAR` | | "Belegte" Symbole (`.`,`^`,`$`)| `\.`,`\^`,`\$` | `DOT`,`CARAT`, `DOLLAR`| * Besondere Muster regelhafte Ausdrücke * Alternative Zeichenfolgen (Alternations) * Zeichenklassen (character classes) * Wiederholungen --- class: left .blockquote[Alternative Zeichenfolgen] ## Unterschiedliche Kombinationen von Zeichenfolgen `(winter|sommer)` ```r library(stringr) library(rebus) or("Winter","Sommer") ``` ``` ## <regex> (?:Winter|Sommer) ``` ```r str_view(c("Sommer-Hitze","Traum-Winter","Traumwinter"), pattern = or("[Ww]inter","[Ss]ommer"), match=TRUE,html=TRUE) ```
??? * Obs! Die erste Ausgabe unterscheidet sich leicht von der Eingabe: rebus ergänzt um `?:`, um anzuzeigen, dass es sich um eine "nicht-einfangende Gruppe" von Zeichen (non-capturing group) handelt. Dazu später mehr --- class: left .blockquote[Zeichenklassen (character classes)] ## Identifikation von (definierten) Symbolen .pull-left[ ```r char_class("Aa") ``` ``` ## <regex> [Aa] ``` ```r str_view(c("Frankfurt","Freiheit","Effekt","Schifffahrt"), pattern = char_class("Ff"),html=TRUE ) ```
] -- .pull-right[ ```r negated_char_class("Ff") ``` ``` ## <regex> [^Ff] ``` ```r str_view(c("Frankfurt","Freiheit","Effekt","Schifffahrt"), pattern = negated_char_class("Ff"),html=TRUE ) ```
] ??? * `char_class()` hilft alle Symbole zu finden * Mit `negated_char_class()` können alle Symbole "außer" die angebebenen identifiziert werden. * dies wird in rebus durch das Carat `^`-Symbol innerhalb der eckigen Klammer angezeigt <!-- --- --> <!-- class: left --> <!-- ## Besonderheiten bei Verwendung von Zeichenklassen --> <!-- .pull-left[ --> <!-- * Minuszeichen --> <!-- ```{r} --> <!-- str_view(c("allgemein","Aaron","Sommer-Hitze"), --> <!-- pattern = char_class("Aa-"),html=TRUE --> <!-- ) --> <!-- ``` --> <!-- ] --> <!-- .pull-right[ --> <!-- <br> --> <!-- ```{r} --> <!-- str_view(c("allgemein","Aaron","Sommer-Hitze"), --> <!-- pattern = char_class("-Aa"),html=TRUE --> <!-- ) --> <!-- ``` --> <!-- ] --> <!-- ??? --> <!-- * **Wichtig:** innerhalb von `char_class()` müssen spezielle Symbole wie bspw. `^` oder `$` **nicht** "maskiert" werden. Ein Punkt `.` kann also direkt eingefügt werden. --> <!-- * Ausnahme: Das Minuszeichen. Wenn es in der `char_class()`-Funktion verwendet werden soll, dann sollte es **an erster Stelle** kommen, also bspw. `char_class("-Aa")` --> --- class: left .blockquote[Wiederholungen] ## Zeichenketten mit mehreren gleichartigen Symbolen/Mustern * Übersicht | Muster | Regelmäßiger Ausdruck | rebus | |----------------------|:---------------------:|:----------:| | Optional | `?` | `optional()` | | Null oder mehr | `*` | `zero_or_more()` | | Eins oder mehr | `+` | `one_or_more()` | | Zwischen m- und n-mal | `{m,n}` | `repeated()`| * Beispiel ```r str_view(c("Frankfurt","Freiheit","Effekt","Schifffahrt"), pattern = repeated("Ff",2,3),html=TRUE ) ```
--- class: left .blockquote[Abkürzungen] ## Abgekürzte Eingabe regelhafter Ausdrücke .pull-left[ * $-Symbol und Ziffer(n): ```r DOLLAR %R% char_class("0123456789") ``` ``` ## <regex> \$[0123456789] ``` ] -- .pull-right[ * Eine Ziffer: ```r char_class("0-9") ``` ``` ## <regex> [0-9] ``` ] -- .pull-left[ * Kleinbuchstaben ```r char_class("a-z") ``` ``` ## <regex> [a-z] ``` ] -- .pull-right[ * Großbuchstaben ```r char_class("A-Z") ``` ``` ## <regex> [A-Z] ``` ] --- class: left .blockquote[Abkürzungen] ## Abgekürzte Eingabe regelhafter Ausdrücke .pull-left[ * Ziffern ```r DGT ``` ``` ## <regex> \d ``` ] -- .pull-right[ <br> ```r char_class("0-9") ``` ``` ## <regex> [0-9] ``` ] -- .pull-left[ * Buchstaben ```r WRD ``` ``` ## <regex> \w ``` ] -- .pull-right[ <br> ```r char_class("a-zA-Z0-0_") ``` ``` ## <regex> [a-zA-Z0-0_] ``` ] -- .pull-left[ * Leerzeichen (Whitespace) ```r SPC ``` ``` ## <regex> \s ``` ] --- class: inverse, center, middle ## Fortgeschrittene regelhafte Ausdrücke .blockquote[Erfassung (Capturing)] .blockquote[Rückbezüge (Backreferences)] .blockquote[Mustererkennung mit Unicode] --- class: left .blockquote[Erfassung (Capturing)] ## Gruppierung von Teilmustern * Dient der *Gruppierung von Teilen* von Mustern * Erfasste Teile (von Mustern) können in darauffolgenden Schritten referenziert werden. .pull-left[ ```r ANY_CHAR %R% "a" ``` ``` ## <regex> .a ``` ] -- .pull-right[ ```r str_extract(c("Fakten","Katzen"), pattern=ANY_CHAR %R% "a") ``` ``` ## [1] "Fa" "Ka" ``` ] -- .pull-left[ * Erfasste Teile (*captured groups*) werden in regulären Ausdrücken durch `()` angezeigt/eingegeben: ```r capture(ANY_CHAR) %R% "a" ``` ``` ## <regex> (.)a ``` ] -- .pull-right[ * Capturing führt vordergründig (!) zum selben Ergebnis: ```r str_extract(c("Fakten","Katzen"), capture(ANY_CHAR) %R% "a") ``` ``` ## [1] "Fa" "Ka" ``` ] ??? * Capturing ändert nicht das Muster, das abgeglichen wird, entsprechend führt es auch zum selben Ergebnis * Aber: capturing zeigt an, dass wir (in darauffolgenden Schritten) etwas mit dem erfassten Muster machen wollen --- class: left .blockquote[Erfassung (Capturing)] ## Verwendung erfasster Teilmuster * Verwendung in `str_match()` liefert eine Matrix, wobei die... * ...erste Spalte dem Ergebnis von `str_extract()` entspricht. * ...zweite Spalte nur den erfassten Teil des Musters enthält. * Beispiel ```r str_match(c("Fakten","Katze"), pattern=capture(ANY_CHAR) %R% "a") ``` ``` ## [,1] [,2] ## [1,] "Fa" "F" ## [2,] "Ka" "K" ``` ??? * Verwendung in `str_match()` liefert eine Matrix * erste Spalte entspricht dem Ergebnis von `str_extract()` **mit dem exakten Match** * die zweite Spalte enthält dagegen nur den erfassten Teil des Musters in der captured group --- class: left .blockquote[Erfassung (Capturing)] ## Anwendungsbeispiel mit erfassten Gruppen * Beispiel: Dollar-Beträge unter 100$ ```r muster <- DOLLAR %R% DGT %R% optional(DGT) %R% DOT %R% dgt(2) str_view(c("$7.70","$27.00"),pattern = muster) ``` ``` ## [1] │ <$7.70> ## [2] │ <$27.00> ``` * Nutzung erfasster Teilmuster zur getrennten Extraktion von Dollar- und Cent-Beträgen: ```r cap_muster <- DOLLAR %R% capture(DGT %R% optional(DGT)) %R% DOT %R% capture(dgt(2)) str_match(c("$7.70","$27.00"), pattern = cap_muster) ``` ``` ## [,1] [,2] [,3] ## [1,] "$7.70" "7" "70" ## [2,] "$27.00" "27" "00" ``` ??? * Obs! capture-Funktion wird zweimal verwendet: * einmal um die Vorkommastelle(n) und * einmal um die Nachkommastellen --- class: left .blockquote[Rückbezüge (Backreferences)] ## Verweis auf erfasste Teile von Mustern ```r REF1 ``` ``` ## <regex> \1 ``` ```r REF2 ``` ``` ## <regex> \2 ``` --- class: left .blockquote[Mustererkennung mit Unicode] ## Unicode --- class: inverse,center,middle # Wir brauchen eine Pause. --- background-image: url("data:image/png;base64,#http://bit.ly/cs631-donkey") background-size: 80% --- class: left ## Quellenverzeichnis .ref-slide[ <a name=bib-wickham_r_2023></a>[Wickham, H., M. Çetinkaya-Rundel, and G. Grolemund](#cite-wickham_r_2023) (2023). _R for Data Science: Import, Tidy, Transform, Visualize, and Model Data_. 2nd Edition. Beijing Boston Farnham Sebastopol Tokyo: O'Reilly. ISBN: 978-1-4920-9740-2. ]